﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AutoMapper;
using System.Reflection;
using System.Data;
using System.Collections;
using System.ComponentModel;
using System.Globalization;

namespace Steve.EFDataLayer
{
    //this should be removed at some point...
    //HACK: bad hack to fix automapper temporarily...
public class FixedDataReaderMapper : IObjectMapper
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
    public static bool IsEnumerableType(Type type)
    {
        return type.GetInterfaces().Contains<Type>(typeof(IEnumerable));
    }

    private static bool IsDataReader(ResolutionContext context)
    {
        return (typeof(IDataReader).IsAssignableFrom(context.SourceType) && IsEnumerableType(context.DestinationType));
    }

    private static bool IsDataRecord(ResolutionContext context)
    {
        return typeof(IDataRecord).IsAssignableFrom(context.SourceType);
    }

    public bool IsMatch(ResolutionContext context)
    {
        return (IsDataReader(context) || IsDataRecord(context));
    }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
    public object Map(ResolutionContext context, IMappingEngineRunner mapper)
    {
        object destination;
        if (IsDataReader(context))
        {
            throw new NotSupportedException();
        }
        if (IsDataRecord(context))
        {
            IDataRecord dataRecord = context.SourceValue as IDataRecord;
            destination = Activator.CreateInstance(context.DestinationType);

            for (int i = 0; i < dataRecord.FieldCount; i++)
            {
                var fieldName = dataRecord.GetName(i);
                var isNull = dataRecord.IsDBNull(i);
                var fieldValue = dataRecord.GetValue(i);

                var destProperty = context.DestinationType.GetProperty(fieldName);
                
                if (destProperty != null && destProperty.GetSetMethod(true) != null)
                {
                    var destPropertyType = destProperty.PropertyType;
                    //bool nullableType = false;

                    if (destPropertyType.IsGenericType && destProperty.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                    {
                        //nullableType = true;
                        destPropertyType = destPropertyType.GetGenericArguments()[0];
                    }

                    if (isNull)
                    {
                        destProperty.SetValue(destination, null, null);
                    }
                    else
                    {
                        var destConverter = TypeDescriptor.GetConverter(destPropertyType);
                        var sourceConverter = TypeDescriptor.GetConverter(fieldValue.GetType());

                        if (destPropertyType.Equals(fieldValue.GetType()))
                        {
                            //do nothing...
                        }
                        else if (destConverter.CanConvertFrom(fieldValue.GetType()))
                            fieldValue = destConverter.ConvertFrom(fieldValue);
                        else if (sourceConverter.CanConvertTo(destPropertyType))
                            fieldValue = sourceConverter.ConvertTo(fieldValue, destPropertyType);
                        else
                            throw new InvalidCastException();

                        destProperty.SetValue(destination, fieldValue, null);
                    }
                }
            }

            MapPropertyValues(context, mapper, destination);
            return destination;
        }
        return null;
    }

    private static void MapPropertyValue(ResolutionContext context, IMappingEngineRunner mapper, object mappedObject, PropertyMap propertyMap)
    {
        if (propertyMap.CanResolveValue())
        {
            ResolutionResult result = propertyMap.ResolveValue(context);
            ResolutionContext context2 = context.CreateMemberContext(null, result.Value, null, result.Type, propertyMap);
            if (propertyMap.ShouldAssignValue(context2))
            {
                try
                {
                    object obj2 = mapper.Map(context2);
                    if (propertyMap.CanBeSet)
                    {
                        propertyMap.DestinationProperty.SetValue(mappedObject, obj2);
                    }
                }
                catch (Exception exception)
                {
                    throw new AutoMapperMappingException(context2, exception);
                }
            }
        }
    }

    private static void MapPropertyValues(ResolutionContext context, IMappingEngineRunner mapper, object result)
    {
        foreach (PropertyMap map in context.TypeMap.GetPropertyMaps())
        {
            MapPropertyValue(context, mapper, result, map);
        }
    }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
    public static Type GetElementType(Type enumerableType, IEnumerable enumerable)
    {
        if (enumerableType.HasElementType)
        {
            return enumerableType.GetElementType();
        }
        if (enumerableType.IsGenericType && enumerableType.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>)))
        {
            return enumerableType.GetGenericArguments()[0];
        }
        Type iEnumerableType = GetIEnumerableType(enumerableType);
        if (iEnumerableType != null)
        {
            return iEnumerableType.GetGenericArguments()[0];
        }
        if (!typeof(IEnumerable).IsAssignableFrom(enumerableType))
        {
            throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unable to find the element type for type '{0}'.", enumerableType), "enumerableType");
        }
        if (enumerable != null)
        {
            object obj2 = enumerable.Cast<object>().FirstOrDefault<object>();
            if (obj2 != null)
            {
                return obj2.GetType();
            }
        }
        return typeof(object);
    }

    private static Type GetIEnumerableType(Type enumerableType)
    {
        try
        {
            return enumerableType.GetInterface("IEnumerable`1", false);
        }
        catch (AmbiguousMatchException)
        {
            if (!(enumerableType.BaseType == typeof(object)))
            {
                return GetIEnumerableType(enumerableType.BaseType);
            }
            return null;
        }
    }

 

 


 

 

}

 
}
